package net.towee.client.com.repo.presenter;

import java.util.List;

import net.towee.client.com.repo.RepositoryDocumentEditor;
import net.towee.client.com.repo.RepositoryNodeSelector;
import net.towee.client.com.repo.RepositoryShower;
import net.towee.client.managers.AttachmentsManager;
import net.towee.client.managers.DocumentsManager;
import net.towee.client.managers.NodesManager;
import net.towee.client.widgets.upload.HasUploadHandlers;
import net.towee.client.widgets.upload.UploadHandler;
import net.towee.model.ProfileArea;
import net.towee.model.account.AccountIdentity;
import net.towee.model.repo.AttachmentIdentity;
import net.towee.model.repo.DocumentData;
import net.towee.model.repo.DocumentIdentity;
import net.towee.model.repo.DocumentNode;
import net.towee.model.repo.DocumentType;
import net.towee.model.repo.NodeData;
import net.towee.model.repo.impl.DocumentDataImpl;
import net.towee.model.repo.impl.DocumentIdentityImpl;
import net.towee.model.repo.impl.DocumentNodeImpl;
import net.towee.model.utils.Pair;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;

public class RepositoryDocumentEditorPresenter implements RepositoryDocumentEditor, ClickHandler,
		UploadHandler, ChangeHandler, AsyncCallback<DocumentData> {

	public interface Display extends IsWidget {
		HasClickHandlers getCreateButton();

		HasClickHandlers getCancelButton();

		HasChangeHandlers getNameInputBox();

		HasUploadHandlers getUploadForm();

		HasWidgets getContainerForNodeSelector();
		
		void setEditMode();

		/*
		 * Getters
		 */
		List<DocumentType> getTypes();

		List<Pair<String, String>> getProfileAreas();

		String getDescription();

		String getName();

		List<AttachmentIdentity> getAtachments();

		String getExplanation();

		/*
		 * Setters
		 */
		void addType(DocumentType type);

		void addProfileArea(String key, String value);

		void setDescription(String description);

		void setName(String name);

		void addAttachment(AttachmentIdentity att);

		void addAttachment(AttachmentIdentity att, String rawId);

		/*
		 * Errors
		 */
		void nameTooShort(int minLength);

		void nameExists();

		void descTooShort(int minLength);

		void explTooShort(int minExplLength);
		
		boolean isFilesAttached();

		boolean hasErrors();

		void setDocumentType(DocumentType type);
	}

	// Injected
	private final DocumentsManager documentsManager;
	private final Display display;
	private final NodesManager nodesManager;
	private final AttachmentsManager attManager;
	private final RepositoryShower shower;
	private final RepositoryNodeSelector nodeSelector;

	// General type of document
	private DocumentData document;

	// Minimal lengths
	private final int minNameLength = 2;
	private final int minDescLength = 10;
	private final int minExplLength = 15;

	// Flag for name checking
	private boolean nameChecked = false;

	@Inject
	public RepositoryDocumentEditorPresenter(Display display, DocumentsManager documentsManager,
			NodesManager nodesManager, AttachmentsManager manager, RepositoryShower shower,
			RepositoryNodeSelector nodeSelector) {
		this.documentsManager = documentsManager;
		this.nodesManager = nodesManager;
		this.attManager = manager;
		this.display = display;
		this.shower = shower;
		this.nodeSelector = nodeSelector;

		display.getCancelButton().addClickHandler(this);
		display.getCreateButton().addClickHandler(this);
		display.getUploadForm().addUploadHandler(this);
		display.getNameInputBox().addChangeHandler(this);
	}

	@Override
	public void go(HasWidgets container) {
		Preconditions.checkNotNull(document, "document for editing not set");

		// Show node selector
		nodeSelector.go(display.getContainerForNodeSelector());

		// Show editor
		container.clear();
		container.add(display.asWidget());
	}

	@Override
	public void setDocument(DocumentData doc, NodeData parentNode) {
		// Set document for editing
		this.document = doc.getIdentity().getId() == null ? doc : new DocumentDataImpl( doc );

		// Add types
		for (DocumentType type : doc.getIdentity().getDocumentTypes().getTypesList()) {
			display.addType(type);
		}

		// Add profile areas
		for (ProfileArea area : doc.getProfile().getProfileAreas()) {
			display.addProfileArea(area.getName(), area.getValue());
		}

		// Set description and name
		display.setDescription(doc.getDescription());
		display.setName(doc.getIdentity().getName());

		// Set attachments
		for (AttachmentIdentity att : doc.getAttachments().getAttachmentList()) {
			display.addAttachment(att);
		}

		// Set node in selector
		nodeSelector.setCurrentNode(parentNode);

		// If we edit some document
		if (doc.getIdentity().getId() != null) {
			nameChecked = true;
			display.setEditMode();
		}
	}

	@Override
	public void setEmptyDocument(DocumentType type, NodeData parentNode, AccountIdentity account) {
		// Create identity
		DocumentIdentity idnt = new DocumentIdentityImpl(null, "", account, null, type);

		// Create empty document data
		DocumentNode node = new DocumentNodeImpl(null, null, idnt);
		node.setParentId(parentNode.getId());
		DocumentData doc = new DocumentDataImpl(idnt, node);

		// Set empty document
		setDocument(doc, parentNode);
	}

	/**
	 * Create document
	 */
	private void createDocument() {
		Preconditions.checkArgument(nameChecked, "name checking in progress or name exists");

		// Validate entered data
		if (display.getDescription().trim().length() < minDescLength)
			display.descTooShort(minDescLength);
		if (display.getName().trim().length() < minNameLength)
			display.nameTooShort(minNameLength);
		if (document.getIdentity().getId() != null
				&& display.getExplanation().trim().length() < minExplLength)
			display.explTooShort(minExplLength);
		if (display.hasErrors() || !display.isFilesAttached())
			return;

		// Set name and description
		document.setDescription(display.getDescription());
		document.getIdentity().setName(display.getName());

		// Set group id
		document.getIdentity().setGroup(nodeSelector.getGroupIdentity());

		// Set attachments
		document.getAttachments().getAttachmentList().clear();
		for (AttachmentIdentity att : display.getAtachments()) {
			document.getAttachments().addAttachment(att);
		}

		// Add profile
		document.getProfile().clearExtendedProfile();
		for (Pair<String, String> area : display.getProfileAreas()) {
			document.getProfile().addProfileArea(area.getFirst(), area.getSecond());
		}

		// Add types
		document.getIdentity().getDocumentTypes().getTypesList().clear();
		for (DocumentType type : display.getTypes()) {
			document.getIdentity().getDocumentTypes().addDocumentType(type);
		}

		// Create or change document
		if (document.getIdentity().getId() == null)
			documentsManager.saveDocument(document, nodeSelector.getSelectedNode().getId(), this);
		else
			documentsManager.changeDocument(document, display.getExplanation(), this);
	}

	@Override
	public void onClick(ClickEvent event) {
		Object source = event.getSource();

		// Show parent node on cancel
		if (source == display.getCancelButton()) {
			if(document.getIdentity().getId() == null)
				shower.showTreeNode(nodeSelector.getSelectedNode());
			else
				shower.showTreeNode(document.getNode());
		}
		// Create document
		else if (source == display.getCreateButton())
			createDocument();
	}

	@Override
	public void onUploaded(final String rawFileId) {
		Preconditions.checkArgument(!Strings.isNullOrEmpty(rawFileId), "null file raw id");

		attManager.createAttachmentIdentity(rawFileId, new AsyncCallback<AttachmentIdentity>() {
			@Override
			public void onFailure(Throwable caught) {
			}

			@Override
			public void onSuccess(AttachmentIdentity result) {
				display.addAttachment(result, rawFileId);
			}
		});
	}

	@Override
	public void onChange(ChangeEvent event) {
		if (document.getIdentity().getId() != null
				&& display.getName().equals(document.getIdentity().getName())) {
			nameChecked = true;
		} else {
			nameChecked = false;
			nodesManager.checkNodeExisting(display.getName(),
					nodeSelector.getSelectedNode().getGroupId(), nodeSelector.getSelectedNode().getId(),
					new AsyncCallback<Boolean>() {
						@Override
						public void onFailure(Throwable caught) {
						}

						@Override
						public void onSuccess(Boolean result) {
							if (result == true)
								display.nameExists();
							else
								nameChecked = true;
						}
					});
		}
	}

	@Override
	public void onFailure(Throwable caught) {
	}

	@Override
	public void onSuccess(DocumentData result) {
		// Show changed/created document
		shower.showTreeNode(result.getNode());
	}

	@Override
	public void setDocumentType(DocumentType type) {
		display.setDocumentType( type );
	}
}